Skip to content

feat(sdk): Flatten the hierarchy of caches in the Event Cache, part 2: Pinned events#6568

Merged
Hywan merged 22 commits into
matrix-org:features/event-cache-refactoringfrom
Hywan:features/event-cache-refactoring-flatten-pinned-events
May 21, 2026
Merged

feat(sdk): Flatten the hierarchy of caches in the Event Cache, part 2: Pinned events#6568
Hywan merged 22 commits into
matrix-org:features/event-cache-refactoringfrom
Hywan:features/event-cache-refactoring-flatten-pinned-events

Conversation

@Hywan
Copy link
Copy Markdown
Member

@Hywan Hywan commented May 12, 2026

This PR is the sequel of #6517. This time, we are “flattening” the pinned-events cache, i.e. it extracts the pinned-event cache from the room cache to move it directly in Caches. The tree goes from:

graph TD;
    EventCache-->Caches;
    Caches-->RoomEventCache;
    RoomEventCache-->RoomEventCacheState;
    RoomEventCacheState-->PinnedEventCache;
    PinnedEventCache-->PinnedEventCacheState;
Loading

to:

graph TD;
    EventCache-->Caches;
    Caches-->RoomEventCache;
    Caches-->PinnedEventsCache;
    RoomEventCache-->RoomEventCacheState;
    PinnedEventsCache-->PinnedEventsCacheState;
Loading

Breaking Changes

First and foremost, PinnedEventCache is renamed PinnedEventsCache (plural form on Event) because it contains… multiple… events… hence the plural.

All methods related to pinned-events on RoomEventCache are removed, for example: RoomEventCache::subscribe_to_pinned_events (side note: funny how we have the plural here 🤪) is now PinnedEventsCache::subscribe.

EventCache now provides 1 new public method: pinned_events to lazily access to the PinnedEventsCache.

New Features or Bug Fixes

Because, yeah, we have new features coming with this refactoring!

  • more related events — Until now, the pinned-events cache was storing some events relating to the pinned-events, such as: replacement (edit), or reference, but annotation (e.g. reaction) was missing. Now this is all supported thanks to the new extract_relation helper introduced in the previous PR. This is part of the new aggregator::aggregate_timeline_for_pinned_events pipeline!
  • deduplication — The previous implementation wasn't deduplicating the events handled by the pinned-events cache. Now it's supported with the deduplicator module!
  • better redaction — Redaction was working… by… mistake let's say. Or it wasn't working entirely. Redaction was handled by the room cache, but not by the pinned-events cache. Because the SQLite backend for the EventCacheStore uses a single table for all the events, when the room cache was redacting an event, it was redacted for all the caches. Solid, good, except the pinned-events cache had an unredacted event in memory! Every in-memory modification is broadcasted to the database, but the opposite direction is not supported. Now, redactions are handled like any other caches, both in-memory and in-database. (Note: all these features will be refactored to be shared by all the caches once the “flattening” is over).

Known Caveats

Sync gaps are not resolved. This was already the case before, and it's not fixed here, but at least it makes it obvious by looking at the code. There is no pagination whatsoever with the pinned-events cache.

How do we resolve that? When the cache loads, for each pinned-event, /relations is called to fetch all its related events. It is efficient except that once the pinned-event cache is created, it stays in memory for the entire lifetime of the Client.

It means that if the pinned-events cache is loaded, then a couple of sync happens, no missing events, good, then a gappy sync: ha, too bad, maybe an event related to a pinned-event is missing.

Solution 1: Generalise the “shriking when no more subscribers” mechanism we have for the room cache, and extend that on the pinned-events cache so that we can reload its state from the network.

Solution 2: When the pinned-events cache detects a gap in the sync (now it's easy, it has its own handle_sync method), we must reset its state by relying on the network only. I personally prefer this solution. A bit bazooka for a fly, but it will be the most reliable for the moment.

Thoughts?



  • I've documented the public API Changes in the appropriate CHANGELOG.md files.
  • This PR was made with the help of AI.

Hywan added 4 commits May 12, 2026 16:25
This patch renames `PinnedEventCache` to `PinnedEventsCache` (and same
for all types having `PinnedEventCache` as a prefix).

Why? Because it's a cache about _pinned-events_, not a single
_pinned-event_ :-).
…`Room`.

This patch changes `PinnedEventsCache::new` to receive a `WeakRoom`
instead of a `Room`. It then returns a `Result<Self>`, with `Err` if the
`WeakRoom` doesn't point to the `Room` anymore. Thus, this patch changes
`get_or_init` by `get_or_try_init`, but this one is unstable. So this
patch switches from `OnceLock` to `OnceCell`, which provides a stable
one. Why this change? Because in a later refactoring, providing a `Room`
is a bit annoying: all we will have is a `WeakRoom`. We could do this
work of upgrading the `WeakRoom` to a `Room`, but it seems akward as all
caches are holding a `WeakRoom` if room is necessary.
@Hywan Hywan force-pushed the features/event-cache-refactoring-flatten-pinned-events branch from 96078c6 to c1aad24 Compare May 12, 2026 14:25
@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 70.46414% with 70 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.84%. Comparing base (80fabde) to head (42fef16).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ix-sdk/src/event_cache/caches/pinned_events/mod.rs 65.90% 35 Missing and 10 partials ⚠️
crates/matrix-sdk/src/event_cache/caches/mod.rs 65.30% 12 Missing and 5 partials ⚠️
crates/matrix-sdk/src/event_cache/mod.rs 75.00% 1 Missing and 3 partials ⚠️
...rates/matrix-sdk-ui/src/timeline/controller/mod.rs 0.00% 0 Missing and 2 partials ⚠️
...es/matrix-sdk/src/event_cache/caches/aggregator.rs 92.85% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@                         Coverage Diff                          @@
##           features/event-cache-refactoring    #6568      +/-   ##
====================================================================
- Coverage                             89.87%   89.84%   -0.04%     
====================================================================
  Files                                   383      384       +1     
  Lines                                107846   107967     +121     
  Branches                             107846   107967     +121     
====================================================================
+ Hits                                  96929    97001      +72     
- Misses                                 7225     7270      +45     
- Partials                               3692     3696       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

…entsCacheState`.

This patch renames `PinnedEventsCacheStateLock` to
`LockedPinnedEventsCacheState` to match other namings in
`RoomEventCache` and `ThreadEventCache` for the sake of consistency.
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 12, 2026

Merging this PR will improve performance by 48.88%

⚡ 1 improved benchmark
✅ 49 untouched benchmarks

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation Restore session [memory store] 169.4 ms 113.8 ms +48.88%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing Hywan:features/event-cache-refactoring-flatten-pinned-events (42fef16) with main (7677f09)1

Open in CodSpeed

Footnotes

  1. No successful run was found on features/event-cache-refactoring (80fabde) during the generation of this report, so main (7677f09) was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Hywan Hywan force-pushed the features/event-cache-refactoring-flatten-pinned-events branch 3 times, most recently from 73fa63f to 41ce2bb Compare May 18, 2026 16:48
Hywan added 16 commits May 19, 2026 12:01
This patch introduces the new type `PinnedEventsCacheUpdateSender` _à
la_ `ThreadEventCacheUpdateSender` or `RoomEventCacheUpdateSender`. It
abstracts the channels to send updates about. Yes, for the moment, it
has a single channel, but it's better to provide the same abstractions
for all caches.
This patch introduces the `PinnedEventsCacheInner` type that is used
inside `PinnedEventsCache` to make it cheap to clone. It was already the
case before with all the fields beind `Arc<_>` but we are about to move
data in their correct place, and thus it will add more `Arc<_>`, which
is not good. Let's adopt the same patterns as the other caches too for
the sake of consistency.
This patch adds `PinnedEventsCacheInner::update_sender`, making
`PinnedEventsCacheState::update_sender` a clone of the former.

This is going to be useful in the next commit for handling pinned-events
in `ResetCaches`.
This patch adds `EventCache::pinned_events` to get the `PinnedEventsCache`.

This patch adds `Caches::pinned_events`. `ResetCaches` also
handle the pinned-events cache. To make it works, this patch adds
`PinnedEventsCache::state`, `PinnedEventsCache::update_sender` and
`PinnedEventsCacheStateLockWriteGuard::reset`. This one is new as the
feature wasn't implemented before!
This patch adds the pipeline for `PinnedEventsCache` via
`aggregator_timeline_for_pinned_events`.
…stead of `RoomEventCache`.

This patch moves `maybe_add_live_related_events` to `handle_timeline`.
`handle_timeline` is called by `handle_(joined|left)_room_update`, which
are themselves called by `Caches`.

This patch also removes other methods used by
`maybe_add_live_related_events` but are no longer useful since
we have the aggregator, namely `extract_relation_target` and
`extract_redaction_target`.

Finally, this patch removes the call to `maybe_add_live_related_events`
in `RoomEventCacheStateLockWriteGuard::post_process_new_events`, which
removes the need to acquire a write lock over the room' state here.
This patch runs the `filter_duplicate_events` function inside
`handle_sync` to deduplicate events related to pinned events.
This patch applies event redactions over pinned events.
…he`.

This patch updates `TimelineFocusKind::PinnedEvents { event_cache }` to
use a `PinnedEventsCache` instead of a `RoomEventCache`.
This patch removes `subscribe_to_pinned_events`, it is now useless.
…ving `RoomEventCache`.

This patch updates R2D2 to fetch the `PinnedEventsCache` from
`EventCache` without using `RoomEventCache`.
This patch removes deadcode about `PinnedEventsCache` in `RoomEventCache`.
@Hywan Hywan force-pushed the features/event-cache-refactoring-flatten-pinned-events branch from 41ce2bb to 42fef16 Compare May 19, 2026 10:04
@Hywan Hywan marked this pull request as ready for review May 19, 2026 12:19
@Hywan Hywan requested a review from a team as a code owner May 19, 2026 12:19
@Hywan Hywan requested review from poljar and removed request for a team May 19, 2026 12:19
Copy link
Copy Markdown
Contributor

@poljar poljar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of nits/questions.

The rest of this looks good to me.

Comment thread crates/matrix-sdk/src/event_cache/caches/aggregator.rs
Comment thread crates/matrix-sdk/src/event_cache/caches/pinned_events/mod.rs
Comment thread crates/matrix-sdk/src/event_cache/caches/pinned_events/mod.rs
@Hywan Hywan merged commit b62812a into matrix-org:features/event-cache-refactoring May 21, 2026
61 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants